home *** CD-ROM | disk | FTP | other *** search
/ ShareWare OnLine 2 / ShareWare OnLine Volume 2 (CMS Software)(1993).iso / os2 / elvis172.zip / tio.c < prev    next >
C/C++ Source or Header  |  1993-01-06  |  22KB  |  1,071 lines

  1. /* tio.c */
  2.  
  3. /* Author:
  4.  *    Steve Kirkendall
  5.  *    14407 SW Teal Blvd. #C
  6.  *    Beaverton, OR 97005
  7.  *    kirkenda@cs.pdx.edu
  8.  */
  9.  
  10.  
  11. /* This file contains terminal I/O functions */
  12.  
  13. #include "config.h"
  14. #include "vi.h"
  15. #include "ctype.h"
  16.  
  17. static int showmsg P_((void));
  18.  
  19. /* This function reads in a line from the terminal. */
  20. int vgets(prompt, buf, bsize)
  21.     int    prompt;    /* the prompt character, or '\0' for none */
  22.     char    *buf;    /* buffer into which the string is read */
  23.     int    bsize;    /* size of the buffer */
  24. {
  25.     int    len;    /* how much we've read so far */
  26.     int    ch;    /* a character from the user */
  27.     int    quoted;    /* is the next char quoted? */
  28.     int    tab;    /* column position of cursor */
  29.     char    widths[132];    /* widths of characters */
  30.     int    word;    /* index of first letter of word */
  31. #ifndef NO_DIGRAPH
  32.     int    erased;    /* 0, or first char of a digraph */
  33. #endif
  34.  
  35.     /* show the prompt */
  36.     move(LINES - 1, 0);
  37.     tab = 0;
  38.     if (prompt)
  39.     {
  40.         addch(prompt);
  41.         tab = 1;
  42.     }
  43.     clrtoeol();
  44.     refresh();
  45.  
  46.     /* read in the line */
  47. #ifndef NO_DIGRAPH
  48.     erased =
  49. #endif
  50.     quoted = len = 0;
  51.     for (;;)
  52.     {
  53. #ifndef NO_ABBR
  54.         if (quoted || mode == MODE_EX)
  55.         {
  56.             ch = getkey(0);
  57.         }
  58.         else
  59.         {
  60.             /* maybe expand an abbreviation while getting key */
  61.             for (word = len; --word >= 0 && !isspace(buf[word]); )
  62.             {
  63.             }
  64.             word++;
  65.             ch = getabkey(WHEN_EX, &buf[word], len - word);
  66.         }
  67. #else
  68.         ch = getkey(0);
  69. #endif
  70. #ifndef NO_EXTENSIONS
  71.         if (ch == ctrl('O'))
  72.         {
  73.             ch = getkey(quoted ? 0 : WHEN_EX);
  74.         }
  75. #endif
  76.  
  77.         /* some special conversions */
  78. #if 0
  79.         if (ch == ctrl('D') && len == 0)
  80.             ch = ctrl('[');
  81. #endif
  82. #ifndef NO_DIGRAPH
  83.         if (*o_digraph && erased != 0 && ch != '\b')
  84.         {
  85.             ch = digraph(erased, ch);
  86.             erased = 0;
  87.         }
  88. #endif
  89.  
  90.         /* inhibit detection of special chars (except ^J) after a ^V */
  91.         if (quoted && ch != '\n')
  92.         {
  93.             ch |= 256;
  94.         }
  95.  
  96.         /* process the character */
  97.         switch(ch)
  98.         {
  99.           case ctrl('V'):
  100.             qaddch('^');
  101.             qaddch('\b');
  102.             quoted = TRUE;
  103.             break;
  104.  
  105.           case ctrl('D'):
  106.             return -1;
  107.  
  108.           case ctrl('['):
  109.           case '\n':
  110. #if OSK
  111.           case '\l':
  112. #else
  113.           case '\r':
  114. #endif
  115.             clrtoeol();
  116.             goto BreakBreak;
  117.  
  118. #ifndef CRUNCH
  119.           case ctrl('U'):
  120.             while (len > 0)
  121.             {
  122.                 len--;
  123.                 while (widths[len]-- > 0)
  124.                 {
  125.                     qaddch('\b');
  126.                     qaddch(' ');
  127.                     qaddch('\b');
  128.                 }
  129.             }
  130.             break;
  131. #endif
  132.  
  133.           case '\b':
  134.             if (len > 0)
  135.             {
  136.                 len--;
  137. #ifndef NO_DIGRAPH
  138.                 erased = buf[len];
  139. #endif
  140.                 for (ch = widths[len]; ch > 0; ch--)
  141.                     addch('\b');
  142.                 if (mode == MODE_EX)
  143.                 {
  144.                     clrtoeol();
  145.                 }
  146.                 tab -= widths[len];
  147.             }
  148.             else
  149.             {
  150.                 return -1;
  151.             }
  152.             break;
  153.  
  154.           default:
  155.             /* strip off quotation bit */
  156.             if (ch & 256)
  157.             {
  158.                 ch &= ~256;
  159.                 qaddch(' ');
  160.                 qaddch('\b');
  161.             }
  162.  
  163.             /* add & echo the char */
  164.             if (len < bsize - 1)
  165.             {
  166.                 if (ch == '\t' && !quoted)
  167.                 {
  168.                     widths[len] = *o_tabstop - (tab % *o_tabstop);
  169.                     addstr("        " + 8 - widths[len]);
  170.                     tab += widths[len];
  171.                 }
  172.                 else if (ch > 0 && ch < ' ') /* > 0 by GB */
  173.                 {
  174.                     addch('^');
  175.                     addch(ch + '@');
  176.                     widths[len] = 2;
  177.                     tab += 2;
  178.                 }
  179.                 else if (ch == '\177')
  180.                 {
  181.                     addch('^');
  182.                     addch('?');
  183.                     widths[len] = 2;
  184.                     tab += 2;
  185.                 }
  186.                 else
  187.                 {
  188.                     addch(ch);
  189.                     widths[len] = 1;
  190.                     tab++;
  191.                 }
  192.                 buf[len++] = ch;
  193.             }
  194.             else
  195.             {
  196.                 beep();
  197.             }
  198.             quoted = FALSE;
  199.         }
  200.     }
  201. BreakBreak:
  202.     refresh();
  203.     buf[len] = '\0';
  204.     return len;
  205. }
  206.  
  207.  
  208. static int    manymsgs; /* This variable keeps msgs from overwriting each other */
  209. static char    pmsg[80]; /* previous message (waiting to be displayed) */
  210.  
  211.  
  212. static int showmsg()
  213. {
  214.     /* if there is no message to show, then don't */
  215.     if (!manymsgs)
  216.         return FALSE;
  217.  
  218.     /* display the message */
  219.     move(LINES - 1, 0);
  220.     if (*pmsg)
  221.     {
  222.         standout();
  223.         qaddch(' ');
  224.         qaddstr(pmsg);
  225.         qaddch(' ');
  226.         standend();
  227.     }
  228.     clrtoeol();
  229.  
  230.     manymsgs = FALSE;
  231.     return TRUE;
  232. }
  233.  
  234.  
  235. void endmsgs()
  236. {
  237.     if (manymsgs)
  238.     {
  239.         showmsg();
  240.         addch('\n');
  241.     }
  242. }
  243.  
  244. /* Write a message in an appropriate way.  This should really be a varargs
  245.  * function, but there is no such thing as vwprintw.  Hack!!!
  246.  *
  247.  * In MODE_EX or MODE_COLON, the message is written immediately, with a
  248.  * newline at the end.
  249.  *
  250.  * In MODE_VI, the message is stored in a character buffer.  It is not
  251.  * displayed until getkey() is called.  msg() will call getkey() itself,
  252.  * if necessary, to prevent messages from being lost.
  253.  *
  254.  * msg("")        - clears the message line
  255.  * msg("%s %d", ...)    - does a printf onto the message line
  256.  */
  257. #ifdef    __STDC__
  258. void msg (char *fmt, ...)
  259. {
  260.     va_list    ap;
  261.     va_start (ap, fmt);
  262. #else
  263. void msg(fmt, arg1, arg2, arg3, arg4, arg5, arg6, arg7)
  264.     char    *fmt;
  265.     long    arg1, arg2, arg3, arg4, arg5, arg6, arg7;
  266. {
  267. #endif
  268.     if (mode != MODE_VI)
  269.     {
  270. #ifdef    __STDC__
  271.         vsprintf (pmsg, fmt, ap);
  272. #else
  273.         sprintf(pmsg, fmt, arg1, arg2, arg3, arg4, arg5, arg6, arg7);
  274. #endif
  275.         qaddstr(pmsg);
  276.         addch('\n');
  277.         exrefresh();
  278.     }
  279.     else
  280.     {
  281.         /* wait for keypress between consecutive msgs */
  282.         if (manymsgs)
  283.         {
  284.             getkey(WHEN_MSG);
  285.         }
  286.  
  287.         /* real message */
  288. #ifdef    __STDC__
  289.         vsprintf (pmsg, fmt, ap);
  290. #else
  291.         sprintf(pmsg, fmt, arg1, arg2, arg3, arg4, arg5, arg6, arg7);
  292. #endif
  293.         if (*fmt)
  294.         {
  295.             manymsgs = TRUE;
  296.         }
  297.     }
  298. #ifdef    __STDC__
  299.     va_end (ap);
  300. #endif
  301. }
  302.  
  303.  
  304. /* This function calls refresh() if the option exrefresh is set */
  305. void exrefresh()
  306. {
  307.     char    *scan;
  308.  
  309.     /* If this ex command wrote ANYTHING set exwrote so vi's  :  command
  310.      * can tell that it must wait for a user keystroke before redrawing.
  311.      */
  312.     for (scan=kbuf; scan<stdscr; scan++)
  313.         if (*scan == '\n')
  314.             exwrote = TRUE;
  315.  
  316.     /* now we do the refresh thing */
  317.     if (*o_exrefresh)
  318.     {
  319.         refresh();
  320.     }
  321.     else
  322.     {
  323.         wqrefresh();
  324.     }
  325.     if (mode != MODE_VI)
  326.     {
  327.         manymsgs = FALSE;
  328.     }
  329. }
  330.  
  331.  
  332. /* This structure is used to store maps and abbreviations.  The distinction
  333.  * between them is that maps are stored in the list referenced by the "maps"
  334.  * pointer, while abbreviations are referenced by the "abbrs" pointer.
  335.  */
  336. typedef struct _map
  337. {
  338.     struct _map    *next;    /* another abbreviation */
  339.     short        len;    /* length of the "rawin" characters */
  340.     short        flags;    /* various flags */
  341.     char        *label;    /* label of the map/abbr, or NULL */
  342.     char        *rawin;    /* the "rawin" characters */
  343.     char        *cooked;/* the "cooked" characters */
  344. } MAP;
  345.  
  346. static char    keybuf[KEYBUFSIZE];
  347. static int    cend;    /* end of input characters */
  348. static int    user;    /* from user through end are chars typed by user */
  349. static int    next;    /* index of the next character to be returned */
  350. static MAP    *match;    /* the matching map, found by countmatch() */
  351. static MAP    *maps;    /* the map table */
  352. #ifndef NO_ABBR
  353. static MAP    *abbrs;    /* the abbreviation table */
  354. #endif
  355.  
  356.  
  357.  
  358. /* ring the terminal's bell */
  359. void beep()
  360. {
  361.     /* do a visible/audible bell */
  362.     if (*o_flash)
  363.     {
  364.         do_VB();
  365.         refresh();
  366.     }
  367.     else if (*o_errorbells)
  368.     {
  369.         tputs("\007", 1, faddch);
  370.     }
  371.  
  372.     /* discard any buffered input, and abort macros */
  373.     next = user = cend;
  374. }
  375.  
  376.  
  377.  
  378. /* This function replaces a "rawin" character sequence with the "cooked" version,
  379.  * by modifying the internal type-ahead buffer.
  380.  */
  381. void execmap(rawlen, cookedstr, visual)
  382.     int    rawlen;        /* length of rawin text -- string to delete */
  383.     char    *cookedstr;    /* the cooked text -- string to insert */
  384.     int    visual;        /* boolean -- chars to be executed in visual mode? */
  385. {
  386.     int    cookedlen;
  387.     char    *src, *dst;
  388.     int    i;
  389.  
  390.     /* find the length of the cooked string */
  391.     cookedlen = strlen(cookedstr);
  392. #ifndef NO_EXTENSIONS
  393.     if (visual)
  394.     {
  395.         cookedlen *= 2;
  396.     }
  397. #endif
  398.  
  399.     /* if too big to fit in type-ahead buffer, then don't do it */
  400.     if (cookedlen + (cend - next) - rawlen > KEYBUFSIZE)
  401.     {
  402.         return;
  403.     }
  404.  
  405.     /* shift to make room for cookedstr at the front of keybuf */
  406.     src = &keybuf[next + rawlen];
  407.     dst = &keybuf[cookedlen];
  408.     i = cend - (next + rawlen);
  409.     if (src >= dst)
  410.     {
  411.         while (i-- > 0)
  412.         {
  413.             *dst++ = *src++;
  414.         }
  415.     }
  416.     else
  417.     {
  418.         src += i;
  419.         dst += i;
  420.         while (i-- > 0)
  421.         {
  422.             *--dst = *--src;
  423.         }
  424.     }
  425.  
  426.     /* insert cookedstr, and adjust offsets */
  427.     cend += cookedlen - rawlen - next;
  428.     user += cookedlen - rawlen - next;
  429.     next = 0;
  430.     for (dst = keybuf, src = cookedstr; *src; )
  431.     {
  432. #ifndef NO_EXTENSIONS
  433.         if (visual)
  434.         {
  435.             *dst++ = ctrl('O');
  436.             cookedlen--;
  437.         }
  438. #endif
  439.         *dst++ = *src++;
  440.     }
  441.  
  442. #ifdef DEBUG2
  443.     {
  444. #include <stdio.h>
  445.         FILE    *debout;
  446.         int        i;
  447.  
  448.         debout = fopen("debug.out", "a");
  449.         fprintf(debout, "After execmap(%d, \"%s\", %d)...\n", rawlen, cookedstr, visual);
  450.         for (i = 0; i < cend; i++)
  451.         {
  452.             if (i == next) fprintf(debout, "(next)");
  453.             if (i == user) fprintf(debout, "(user)");
  454.             if (UCHAR(keybuf[i]) < ' ')
  455.                 fprintf(debout, "^%c", keybuf[i] ^ '@');
  456.             else
  457.                 fprintf(debout, "%c", keybuf[i]);
  458.         }
  459.         fprintf(debout, "(end)\n");
  460.         fclose(debout);
  461.     }
  462. #endif
  463. }
  464.  
  465. #ifndef NO_CURSORSHAPE
  466. /* made global so that suspend_curses() can reset it.  -nox */
  467. int    oldcurs;
  468. #endif
  469. /* This function calls ttyread().  If necessary, it will also redraw the screen,
  470.  * change the cursor shape, display the mode, and update the ruler.  If the
  471.  * number of characters read is 0, and we didn't time-out, then it exits because
  472.  * we've apparently reached the end of an EX script.
  473.  */
  474. static int fillkeybuf(when, timeout)
  475.     int    when;    /* mixture of WHEN_XXX flags */
  476.     int    timeout;/* timeout in 1/10 second increments, or 0 */
  477. {
  478.     int    nkeys;
  479. #ifndef NO_SHOWMODE
  480.     static int    oldwhen;    /* "when" from last time */
  481.     static int    oldleft;
  482.     static long    oldtop;
  483.     static long    oldnlines;
  484.     char        *str;
  485. #endif
  486.  
  487. #ifdef DEBUG
  488.     watch();
  489. #endif
  490.  
  491.  
  492. #ifndef NO_CURSORSHAPE
  493.     /* make sure the cursor is the right shape */
  494.     if (has_CQ)
  495.     {
  496.         if (when != oldcurs)
  497.         {
  498.             switch (when)
  499.             {
  500.               case WHEN_EX:        do_CX();    break;
  501.               case WHEN_VICMD:    do_CV();    break;
  502.               case WHEN_VIINP:    do_CI();    break;
  503.               case WHEN_VIREP:    do_CR();    break;
  504.             }
  505.             oldcurs = when;
  506.         }
  507.     }
  508. #endif
  509.  
  510. #ifndef NO_SHOWMODE
  511.     /* if "showmode" then say which mode we're in */
  512.     if (*o_smd && (when & WHENMASK))
  513.     {
  514.         /* redraw the screen before we check to see whether the
  515.          * "showmode" message needs to be redrawn.
  516.          */
  517.         redraw(cursor, !(when & WHEN_VICMD));
  518.  
  519.         /* now the "topline" test should be valid */
  520.         if (when != oldwhen
  521.          || topline != oldtop
  522.          || leftcol != oldleft
  523.          || nlines != oldnlines)
  524.         {
  525.             oldwhen = when;
  526.             oldtop = topline;
  527.             oldleft = leftcol;
  528.             oldnlines = nlines;
  529.  
  530.             if (when & WHEN_VICMD)        str = "Command";
  531.             else if (when & WHEN_VIINP) str = " Input ";
  532.             else if (when & WHEN_VIREP) str = "Replace";
  533.             else if (when & WHEN_REP1)  str = "Replc 1";
  534.             else if (when & WHEN_CUT)   str = "Buffer ";
  535.             else if (when & WHEN_MARK)  str = " Mark  ";
  536.             else if (when & WHEN_CHAR)  str = "Dest Ch";
  537.             else                str = (char *)0;
  538.  
  539.             if (str)
  540.             {
  541.                 move(LINES - 1, COLS - 10);
  542.                 standout();
  543.                 qaddstr(str);
  544.                 standend();
  545.             }
  546.         }
  547.     }
  548. #endif
  549.  
  550. #ifndef NO_EXTENSIONS
  551.     /* maybe display the ruler */
  552.     if (*o_ruler && (when & (WHEN_VICMD|WHEN_VIINP|WHEN_VIREP)))
  553.     {
  554.         char    buf[20];
  555.  
  556.         redraw(cursor, !(when & WHEN_VICMD));
  557.         pfetch(markline(cursor));
  558.         sprintf(buf, "%7ld,%-4d", markline(cursor), 1 + idx2col(cursor, ptext, when & (WHEN_VIINP|WHEN_VIREP)));
  559.         move(LINES - 1, COLS - 22);
  560.         addstr(buf);
  561.     }
  562. #endif
  563.  
  564.     /* redraw, so the cursor is in the right place */
  565.     if (when & WHENMASK)
  566.     {
  567.         redraw(cursor, !(when & (WHENMASK & ~(WHEN_VIREP|WHEN_VIINP))));
  568.     }
  569.  
  570.     /* Okay, now we can finally read the rawin keystrokes */
  571.     refresh();
  572.     nkeys = ttyread(keybuf + cend, sizeof keybuf - cend, timeout);
  573.  
  574.     /* if nkeys == 0 then we've reached EOF of an ex script. */
  575.     if (nkeys == 0 && timeout == 0)
  576.     {
  577.         tmpabort(TRUE);
  578.         move(LINES - 1, 0);
  579.         clrtoeol();
  580.         refresh();
  581.         endwin();
  582.         exit(exitcode);
  583.     }
  584.  
  585.     cend += nkeys;
  586.     user += nkeys;
  587.     return nkeys;
  588. }
  589.  
  590.  
  591. /* This function counts the number of maps that could match the characters
  592.  * between &keybuf[next] and &keybuf[cend], including incomplete matches.
  593.  * The longest comlete match is remembered via the "match" variable.
  594.  */
  595. static int countmatch(when)
  596.     int    when;    /* mixture of WHEN_XXX flags */
  597. {
  598.     MAP    *map;
  599.     int    count;
  600.  
  601.     /* clear the "match" variable */
  602.     match = (MAP *)0;
  603.  
  604.     /* check every map */
  605.     for (count = 0, map = maps; map; map = map->next)
  606.     {
  607.         /* can't match if wrong mode */
  608.         if ((map->flags & when) == 0)
  609.         {
  610.             continue;
  611.         }
  612.  
  613.         /* would this be a complete match? */
  614.         if (map->len <= cend - next)
  615.         {
  616.             /* Yes, it would be.  Now does it really match? */
  617.             if (!strncmp(map->rawin, &keybuf[next], map->len))
  618.             {
  619.                 count++;
  620.  
  621.                 /* if this is the longest complete match,
  622.                  * then remember it.
  623.                  */
  624.                 if (!match || match->len < map->len)
  625.                 {
  626.                     match = map;
  627.                 }
  628.             }
  629.         }
  630.         else
  631.         {
  632.             /* No, it wouldn't.  But check for partial match */
  633.             if (!strncmp(map->rawin, &keybuf[next], cend - next))
  634.             {
  635.                 /* increment by 2 instead of 1 so that, in the
  636.                  * event that we have a partial match with a
  637.                  * single map, we don't mistakenly assume we
  638.                  * have resolved the map yet.
  639.                  */
  640.                 count += 2;
  641.             }
  642.         }
  643.     }
  644.     return count;
  645. }
  646.  
  647.  
  648. #ifndef NO_ABBR
  649. /* This function checks to see whether a word is an abbreviation.  If it is,
  650.  * then an appropriate number of backspoace characters is inserted into the
  651.  * type-ahead buffer, followed by the expanded form of the abbreviation.
  652.  */
  653. static void expandabbr(word, wlen)
  654.     char    *word;
  655.     int    wlen;
  656. {
  657.     MAP    *abbr;
  658.  
  659.     /* if the next character wouldn't end the word, then don't expand */
  660.     if (isalnum(keybuf[next]) || keybuf[next] == ctrl('V') || keybuf[next] == '\b')
  661.     {
  662.         return;
  663.     }
  664.  
  665.     /* find the abbreviation, if any */
  666.     for (abbr = abbrs;
  667.          abbr && (abbr->len != wlen || strncmp(abbr->rawin, word, wlen));
  668.          abbr = abbr->next)
  669.     {
  670.     }
  671.  
  672.     /* If an abbreviation was found, then expand it by inserting the long
  673.      * version into the type-ahead buffer, and then inserting (in front of
  674.      * the long version) enough backspaces to erase to the short version.
  675.      */
  676.     if (abbr)
  677.     {
  678.         execmap(0, abbr->cooked, FALSE);
  679.         while (wlen > 15)
  680.         {
  681.             execmap(0, "\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b", FALSE);
  682.             wlen -= 15;
  683.         }
  684.         if (wlen > 0)
  685.         {
  686.             execmap(0, "\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b" + 15 - wlen, FALSE);
  687.         }
  688.     }
  689. }
  690. #endif
  691.  
  692.  
  693. /* This function calls getabkey() without attempting to expand abbreviations */
  694. int getkey(when)
  695.     int    when;    /* mixture of WHEN_XXX flags */
  696. {
  697.     return getabkey(when, "", 0);
  698. }
  699.  
  700.  
  701. /* This is it.  This function returns keystrokes one-at-a-time, after mapping
  702.  * and abbreviations have been taken into account.
  703.  */
  704. int getabkey(when, word, wlen)
  705.     int    when;    /* mixture of WHEN_XXX flags */
  706.     char    *word;    /* a word that may need to be expanded as an abbr */
  707.     int    wlen;    /* length of "word" -- since "word" might not have \0 */
  708. {
  709.     int    matches;
  710.  
  711.     /* if this key is needed for delay between multiple error messages,
  712.      * then reset the manymsgs flag and abort any mapped key sequence.
  713.      */
  714.     if (showmsg())
  715.     {
  716.         if (when == WHEN_MSG)
  717.         {
  718. #ifndef CRUNCH
  719.             if (!*o_more)
  720.             {
  721.                 refresh();
  722.                 return ' ';
  723.             }
  724. #endif
  725.             qaddstr("[More...]");
  726.             refresh();
  727.             execmap(user, "", FALSE);
  728.         }
  729.     }
  730.  
  731. #ifdef DEBUG
  732.     /* periodically check for screwed up internal tables */
  733.     watch();
  734. #endif
  735.  
  736.     /* if buffer empty, read some characters without timeout */
  737.     if (next >= cend)
  738.     {
  739.         next = user = cend = 0;
  740.         fillkeybuf(when, 0);
  741.     }
  742.  
  743.     /* try to map the key, unless already mapped and not ":set noremap" */
  744.     if (next <= user || *o_remap)
  745.     {
  746.         do
  747.         {
  748.             do
  749.             {
  750.                 matches = countmatch(when);
  751.             } while (matches > 1 && fillkeybuf(when, *o_keytime) > 0);
  752.             if (matches == 1)
  753.             {
  754.                 execmap(match->len, match->cooked,
  755.                     (match->flags & WHEN_INMV) != 0 
  756.                      && (when & (WHEN_VIINP|WHEN_VIREP)) != 0);
  757.             }
  758.         } while (*o_remap && matches == 1);
  759.     }
  760.  
  761.     /* ERASEKEY should always be mapped to '\b'. */
  762.     if (keybuf[next] == ERASEKEY)
  763.     {
  764.         keybuf[next] = '\b';
  765.     }
  766.  
  767. #ifndef NO_ABBR
  768.     /* try to expand an abbreviation, except in visual command mode */
  769.     if (wlen > 0 && (mode & (WHEN_EX|WHEN_VIINP|WHEN_VIREP)) != 0)
  770.     {
  771.         expandabbr(word, wlen);
  772.     }
  773. #endif
  774.  
  775.     /* return the next key */
  776.     return keybuf[next++];
  777. }
  778.  
  779. /* This function maps or unmaps a key */
  780. void mapkey(rawin, cooked, when, name)
  781.     char    *rawin;    /* the input key sequence, before mapping */
  782.     char    *cooked;/* after mapping -- or NULL to remove map */
  783.     int    when;    /* bitmap of when mapping should happen */
  784.     char    *name;    /* name of the key, NULL for no name, "abbr" for abbr */
  785. {
  786.     MAP    **head;    /* head of list of maps or abbreviations */
  787.     MAP    *scan;    /* used for scanning through the list */
  788.     MAP    *prev;    /* used during deletions */
  789.  
  790.     /* Is this a map or an abbreviation?  Choose the right list. */
  791. #ifndef NO_ABBR
  792.     head = ((!name || strcmp(name, "abbr")) ? &maps : &abbrs);
  793. #else
  794.     head = &maps;
  795. #endif
  796.  
  797.     /* try to find the map in the list */
  798.     for (scan = *head, prev = (MAP *)0;
  799.          scan && (strcmp(rawin, scan->rawin) && strcmp(rawin, scan->cooked) ||
  800.         !(scan->flags & when & (WHEN_EX|WHEN_VICMD|WHEN_VIINP|WHEN_VIREP)));
  801.          prev = scan, scan = scan->next)
  802.     {
  803.     }
  804.  
  805.     /* trying to map? (not unmap) */
  806.     if (cooked && *cooked)
  807.     {
  808.         /* if map starts with "visual ", then mark it as a visual map */
  809.         if (head == &maps && !strncmp(cooked, "visual ", 7))
  810.         {
  811.             cooked += 7;
  812.             when |= WHEN_INMV;
  813.         }
  814.  
  815.         /* "visual" maps always work in input mode */
  816.         if (when & WHEN_INMV)
  817.         {
  818.             when |= WHEN_VIINP|WHEN_VIREP|WHEN_POPUP;
  819.         }
  820.  
  821.         /* if not already in the list, then allocate a new structure */
  822.         if (!scan)
  823.         {
  824.             scan = (MAP *)malloc(sizeof(MAP));
  825.             scan->len = strlen(rawin);
  826.             scan->rawin = malloc((unsigned)(scan->len + 1));
  827.             strcpy(scan->rawin, rawin);
  828.             scan->flags = when;
  829.             scan->label = name;
  830.             if (*head)
  831.             {
  832.                 prev->next = scan;
  833.             }
  834.             else
  835.             {
  836.                 *head = scan;
  837.             }
  838.             scan->next = (MAP *)0;
  839.         }
  840.         else /* recycle old structure */
  841.         {
  842.             _free_(scan->cooked);
  843.         }
  844.         scan->cooked = malloc((unsigned)(strlen(cooked) + 1));
  845.         strcpy(scan->cooked, cooked);
  846.     }
  847.     else /* unmapping */
  848.     {
  849.         /* if nothing to unmap, then exit silently */
  850.         if (!scan)
  851.         {
  852.             return;
  853.         }
  854.  
  855.         /* unlink the structure from the list */
  856.         if (prev)
  857.         {
  858.             prev->next = scan->next;
  859.         }
  860.         else
  861.         {
  862.             *head = scan->next;
  863.         }
  864.  
  865.         /* free it, and the strings that it refers to */
  866.         _free_(scan->rawin);
  867.         _free_(scan->cooked);
  868.         _free_(scan);
  869.     }
  870. }
  871.  
  872.  
  873. /* This function returns a printable version of a string.  It uses tmpblk.c */
  874. char *printable(str)
  875.     char    *str;    /* the string to convert */
  876. {
  877.     char    *build;    /* used for building the string */
  878.  
  879.     for (build = tmpblk.c; *str; str++)
  880.     {
  881. #if AMIGA
  882.         if (*str == '\233')
  883.         {
  884.             *build++ = '<';
  885.             *build++ = 'C';
  886.             *build++ = 'S';
  887.             *build++ = 'I';
  888.             *build++ = '>';
  889.         } else 
  890. #endif
  891.         if (UCHAR(*str) < ' ' || *str == '\177')
  892.         {
  893.             *build++ = '^';
  894.             *build++ = *str ^ '@';
  895.         }
  896.         else
  897.         {
  898.             *build++ = *str;
  899.         }
  900.     }
  901.     *build = '\0';
  902.     return tmpblk.c;
  903. }
  904.  
  905. /* This function displays the contents of either the map table or the
  906.  * abbreviation table.  User commands call this function as follows:
  907.  *    :map    dumpkey(WHEN_VICMD, FALSE);
  908.  *    :map!    dumpkey(WHEN_VIREP|WHEN_VIINP, FALSE);
  909.  *    :abbr    dumpkey(WHEN_VIINP|WHEN_VIREP, TRUE);
  910.  *    :abbr!    dumpkey(WHEN_EX|WHEN_VIINP|WHEN_VIREP, TRUE);
  911.  */
  912. void dumpkey(when, abbr)
  913.     int    when;    /* WHEN_XXXX of mappings to be dumped */
  914.     int    abbr;    /* boolean: dump abbreviations instead of maps? */
  915. {
  916.     MAP    *scan;
  917.     char    *str;
  918.     int    len;
  919.  
  920. #ifndef NO_ABBR
  921.     for (scan = (abbr ? abbrs : maps); scan; scan = scan->next)
  922. #else
  923.     for (scan = maps; scan; scan = scan->next)
  924. #endif
  925.     {
  926.         /* skip entries that don't match "when" */
  927.         if ((scan->flags & when) == 0)
  928.         {
  929.             continue;
  930.         }
  931.  
  932.         /* dump the key label, if any */
  933.         if (!abbr)
  934.         {
  935.             len = 8;
  936.             if (scan->label)
  937.             {
  938.                 qaddstr(scan->label);
  939.                 len -= strlen(scan->label);
  940.             }
  941.             do
  942.             {
  943.                 qaddch(' ');
  944.             } while (len-- > 0);
  945.         }
  946.  
  947.         /* dump the rawin version */
  948.         str = printable(scan->rawin);
  949.         qaddstr(str);
  950.         len = strlen(str);
  951.         do
  952.         {
  953.             qaddch(' ');
  954.         } while (len++ < 8);
  955.             
  956.         /* dump the mapped version */
  957. #ifndef NO_EXTENSIONS
  958.         if ((scan->flags & WHEN_INMV) && (when & (WHEN_VIINP|WHEN_VIREP)))
  959.         {
  960.             qaddstr("visual ");
  961.         }
  962. #endif
  963.         str = printable(scan->cooked);
  964.         qaddstr(str);
  965.         addch('\n');
  966.         exrefresh();
  967.     }
  968. }
  969.  
  970. #ifndef NO_MKEXRC
  971.  
  972. static void safequote(str)
  973.     char    *str;
  974. {
  975.     char    *build;
  976.  
  977.     build = tmpblk.c + strlen(tmpblk.c);
  978.     while (*str)
  979.     {
  980.         if (*str <= ' ' && *str >= 1 || *str == '|')
  981.         {
  982.             *build++ = ctrl('V');
  983.         }
  984.         *build++ = *str++;
  985.     }
  986.     *build = '\0';
  987. }
  988.  
  989. /* This function saves the contents of either the map table or the
  990.  * abbreviation table into a file.  Both the "bang" and "no bang" versions
  991.  * are saved.
  992.  *    :map    dumpkey(WHEN_VICMD, FALSE);
  993.  *    :map!    dumpkey(WHEN_VIREP|WHEN_VIINP, FALSE);
  994.  *    :abbr    dumpkey(WHEN_VIINP|WHEN_VIREP, TRUE);
  995.  *    :abbr!    dumpkey(WHEN_EX|WHEN_VIINP|WHEN_VIREP, TRUE);
  996.  */
  997. void
  998. savemaps(fd, abbr)
  999.     int    fd;    /* file descriptor of an open file to write to */
  1000.     int    abbr;    /* boolean: do abbr table? (else do map table) */
  1001. {
  1002.     MAP    *scan;
  1003.     int    bang;
  1004.     int    when;
  1005.  
  1006. # ifndef NO_ABBR
  1007.     for (scan = (abbr ? abbrs : maps); scan; scan = scan->next)
  1008. # else
  1009.     for (scan = maps; scan; scan = scan->next)
  1010. # endif
  1011.     {
  1012.         /* skip maps that have labels, except for function keys */
  1013.         if (scan->label && *scan->label != '#')
  1014.         {
  1015.             continue;
  1016.         }
  1017.  
  1018.         for (bang = 0; bang < 2; bang++)
  1019.         {
  1020.             /* decide which "when" flags we want */
  1021. # ifndef NO_ABBR
  1022.             if (abbr)
  1023.                 when = (bang ? WHEN_EX|WHEN_VIINP|WHEN_VIREP : WHEN_VIINP|WHEN_VIREP);
  1024.             else
  1025. # endif
  1026.                 when = (bang ? WHEN_VIREP|WHEN_VIINP : WHEN_VICMD);
  1027.  
  1028.             /* skip entries that don't match "when" */
  1029.             if ((scan->flags & when) == 0)
  1030.             {
  1031.                 continue;
  1032.             }
  1033.  
  1034.             /* write a "map" or "abbr" command name */
  1035. # ifndef NO_ABBR
  1036.             if (abbr)
  1037.                 strcpy(tmpblk.c, "abbr");
  1038.             else
  1039. # endif
  1040.                 strcpy(tmpblk.c, "map");
  1041.  
  1042.             /* maybe write a bang.  Definitely write a space */
  1043.             if (bang)
  1044.                 strcat(tmpblk.c, "! ");
  1045.             else
  1046.                 strcat(tmpblk.c, " ");
  1047.  
  1048.             /* write the rawin version */
  1049. # ifndef NO_FKEY
  1050.             if (scan->label)
  1051.                 strcat(tmpblk.c, scan->label);
  1052.             else
  1053. # endif
  1054.                 safequote(scan->rawin);
  1055.             strcat(tmpblk.c, " ");
  1056.                 
  1057.             /* dump the mapped version */
  1058. # ifndef NO_EXTENSIONS
  1059.             if ((scan->flags & WHEN_INMV) && (when & (WHEN_VIINP|WHEN_VIREP)))
  1060.             {
  1061.                 strcat(tmpblk.c, "visual ");
  1062.             }
  1063. # endif
  1064.             safequote(scan->cooked);
  1065.             strcat(tmpblk.c, "\n");
  1066.             twrite(fd, tmpblk.c, strlen(tmpblk.c));
  1067.         }
  1068.     }
  1069. }
  1070. #endif
  1071.